<?php
/**
 * The MIT License
 *
 * Copyright (c) <year> <copyright holders>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 * @author Damiano Ciarla
 * @package plugin
 * @subpackage entity_manager/class
 */
class EntityManager_EntityProperty
{
	/**
	 * Entity sulla quale effettuare le operazioni
	 *
	 * @var EntityManager_Abstract_Entity
	 */
	private static $entity = null;

	/**
	 * Istanza di questa classe per realizzare il pattern Singleton
	 *
	 * @var EntityManager_EntityProperty
	 */
	private static $instance = null;

	/**
	 * Metodo per implementare il pattern Singleton per restituire l'istanza della classe EntityManager_EntityProperty
	 *
	 * @param EntityManager_Abstract_Entity $entity Istanza della entity su cui applicare i metodi presenti in questa classe
	 *
	 * @return EntityManager_EntityProperty Istanza della classe EntityManager_EntityProperty
	 */
	public static function getInstance(EntityManager_Abstract_Entity $entity)
	{
		if (self::$instance === null)
			self::$instance = new EntityManager_EntityProperty();

		self::$entity = $entity;

		return self::$instance;
	}

	/**
	 * Metodo che restituisce tutte le proprietà della entity
	 *
	 * @return array
	 */
	public function getProperty()
	{
		$listProperty = array();

		// Creo una Reflection per la entity in esame
		$entityReflection = new ReflectionClass(self::$entity);

		// Recupero le informazioni della classe Base della entity
		$baseEntityReflection = $entityReflection->getParentClass();

		$listProperty["ENTITY"] = $this->_parsePropertyDoc($baseEntityReflection, &$listProperty);
		$listProperty["FIELDS"] = array();

		foreach($entityReflection->getProperties() as $property)
		{
			// Recupero il nome del campo
			$nameVar = $property->getName();

			$listProperty["FIELDS"][$nameVar] = array(
				"value" => self::$entity->$nameVar,
				"properties" => $this->_parsePropertyDoc($property, &$listProperty)
			);
		}

		return $listProperty;
	}

	/**
	 * Metodo che restituisce la lista delle proprietà aggiornata dei soli valori
	 * eventualmenti modificati nella vita della entity
	 *
	 * @param array $property Lista delle proprietà della entity
	 *
	 * @return array
	 */
	public function updatePropertyValues($property)
	{
		foreach($property["FIELDS"] as $fieldName => $infoField)
			$property["FIELDS"][$fieldName]["value"] = self::$entity->$fieldName;

		return $property;
	}

	/**
	 * Metodo che restituisce il risultato della validazione della entity. Se nel parametro
	 * viene specificato un valore uguale 'true' il ritorno sarà un array contenente la
	 * lista degli errori trovati nella validazione
	 *
	 * @param boolean $getErrors
	 *
	 * @return boolean|array
	 */
	public function validate($getErrors)
	{
		// Valido la entity selezionata
		$validator = new EntityManager_Validator(self::$entity);

		// Recupero il risultato della validazione
		$result = $validator->validate();

		return (($getErrors) ? $validator->getErrors() : $result);
	}

	/**
	 * Metodo che restituisce la lista delle proprietà trovate nella documentazione
	 * della entity dove vengono specificati le proprietà dei campi e della tabella
	 *
	 * @param ReflectionProperty|ReflectionClass $Property
	 * @param array $listProperty
	 *
	 * @return array
	 */
	private function _parsePropertyDoc($Property, $listProperty)
	{
		$result = array();

		$docComment = $Property->getDocComment();

		if (trim($docComment) == '')
			return null;

		$docComment = preg_replace('#[ \t]*(?:\/\*\*|\*\/|\*)?[ ]{0,1}(.*)?#', '$1', $docComment);
		$docComment = ltrim($docComment, "\r\n");

		while (($newlinePos = strpos($docComment, "\n")) !== false)
		{
			$line = substr($docComment, 0, $newlinePos);

			$matches = array();

			if ((strpos($line, '@') === 0) && (preg_match('#^(@\w+.*?)(\n)(?:@|\r?\n|$)#s', $docComment, $matches)))
			{
				$docCommentLine = $matches[1];

				if (!preg_match('#^@(\w+)(\s|$)#', $docCommentLine))
					break;

				$value = preg_match('/"(.*)"/', $docCommentLine, $val);
				$value = (($value) ? $val[1] : null);

				$dataBlock = (explode(" ", substr($docCommentLine, 1)));

				switch($dataBlock[0])
				{
					case "TABLE":
						$result[$dataBlock[0]] = $value;
						break;

					case "PRIMARY":
						$result[$dataBlock[0]] = true;
						$listProperty["ENTITY"]["PRIMARY"][] = $Property->getName();
						break;

					case "AUTO_INCREMENT":
						$result[$dataBlock[0]] = true;
						$listProperty["ENTITY"]["AUTO_INCREMENT"] = $Property->getName();
						break;

					case "NOT_NULL":
						$result[$dataBlock[0]] = true;
						break;

					case "JOIN_TO":
					case "JOIN_FROM":
						$join = ((strpos($value, "<")) ? explode(" < ", $value) : explode(" > ", $value));
						$tableReferenced = explode(".", $join[1]);

						$result[$dataBlock[0]][] = array(
							"FIELD" => $join[0],
							"JOIN" => array(
								"TABLE" => $tableReferenced[0],
								"FIELD" => $tableReferenced[1]
							)
						);
						break;

					case "GET":
						$data = explode(" > ", $value);

						$objTable = explode("|", trim($data[0]));
						$fields = explode("|", trim($data[1]));

						$result[$dataBlock[0]][$objTable[0]] = array(
							"TABLE" => $objTable[1],
							"SELF_FIELD" => $fields[0],
							"TO_FIELD" => $fields[1]
						);
						break;

					case "COLUMN":
					case "DEFAULT":
					case "TYPE":
						$result[$dataBlock[0]] = $value;
						break;

					case "LENGTH":
						$result[$dataBlock[0]] = ((!strpos($value, ",")) ? intval($value) : explode(",", str_replace("'", "", $value)));
						break;
				}

				$docComment =  str_replace($matches[1] . $matches[2], '', $docComment);
			}
		}

		return $result;
	}
}
?>
